//
//  ToxManager.swift
//  Mixin
//
//  Created by zhanghanbing on 2018/8/29.
//  Copyright © 2018年 Insight. All rights reserved.
//

import Foundation
import RxSwift

class ToxManager {
    static let shared = ToxManager()
    
    private(set) var toxMananger: OCTManager?
    
    var currentUserStatus: UserStatus {
        guard let toxMananger = toxMananger else {
            return .offline
        }
        return UserStatus(connectionStatus: toxMananger.user.connectionStatus, userStatus: toxMananger.user.userStatus)
    }
    
    func createProfile(_ profileName: String, copyFromURL: URL?, password: String?) -> Bool {
        if profileName.isEmpty {
            return false
        }
        
        let profileManager = ProfileManager()
        
        do {
            try profileManager.createProfileWithName(profileName, copyFromURL: copyFromURL)
        }
        catch {
            return false
        }
        
        return true
    }
    
    func tryLogin() -> Observable<Bool> {
        guard let password = KeychainManager().toxPasswordForActiveAccount else {
            return Observable.just(false)
        }
        
        let deleteActiveAccountAndRetry: () -> Void = {
            KeychainManager().deleteActiveAccountData()
        }
        
        guard let profileName = UserDefaultsManager().lastActiveProfile else {
            deleteActiveAccountAndRetry()
            return Observable.just(false)
        }
        
        let path = ProfileManager().pathForProfileWithName(profileName)
        
        guard let configuration = OCTManagerConfiguration.configurationWithBaseDirectory(path) else {
            deleteActiveAccountAndRetry()
            return Observable.just(false)
        }
        
        return Observable.create { observer -> Disposable in
            ToxFactory.createToxWithConfiguration(configuration,
                                                  encryptPassword: password,
                                                  successBlock: { manager in
                                                    self.toxMananger = manager
                                                    self.toxMananger?.bootstrap.addPredefinedNodes()
                                                    self.toxMananger?.bootstrap.bootstrap()
                                                    observer.onNext(true)
                                                    observer.onCompleted()
            },
                                                  failureBlock: { _ in
                                                    print("Cannot create tox with configuration \(configuration)")
                                                    deleteActiveAccountAndRetry()
                                                    observer.onNext(false)
                                                    observer.onCompleted()
            })
            
            return Disposables.create()
        }
    }
    
    func login(profile: String, nickname: String? = nil, password: String) -> Observable<Void> {
        return Observable.create { observer -> Disposable in
            let path = ProfileManager().pathForProfileWithName(profile)
            guard let configuration = OCTManagerConfiguration.configurationWithBaseDirectory(path) else {
                observer.onError(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Login fail", comment: "")]))
                return Disposables.create()
            }
            
            ToxFactory.createToxWithConfiguration(configuration, encryptPassword: password, successBlock: { [weak self] manager -> Void in
                if let nickname = nickname {
                    _ = try? manager.user.setUserName(nickname)
                }
                
                self?.toxMananger = manager
                self?.toxMananger?.bootstrap.addPredefinedNodes()
                self?.toxMananger?.bootstrap.bootstrap()
                
                KeychainManager().toxPasswordForActiveAccount = password
                let userDefaults = UserDefaultsManager()
                userDefaults.lastActiveProfile = profile
                
                observer.onNext(())
                observer.onCompleted()
                }, failureBlock: { error -> Void in
                    observer.onError(error)
            })
            
            return Disposables.create()
        }
    }
    
    private func isProfileEncrypted(_ profile: String) -> Bool {
        let profilePath = ProfileManager().pathForProfileWithName(profile)
        
        let configuration = OCTManagerConfiguration.configurationWithBaseDirectory(profilePath)!
        let dataPath = configuration.fileStorage.pathForToxSaveFile
        
        guard FileManager.default.fileExists(atPath: dataPath) else {
            return false
        }
        
        guard let data = try? Data(contentsOf: URL(fileURLWithPath: dataPath)) else {
            return false
        }
        
        return OCTToxEncryptSave.isDataEncrypted(data)
    }
    
    func logout() {
        toxMananger = nil
        KeychainManager().deleteActiveAccountData()
    }
}
